package com.cvte.sunkit.tools

import android.util.Log
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.util.regex.Pattern
import kotlin.math.abs
import kotlin.math.ceil
import kotlin.math.pow
import kotlin.math.round


object FpsTool {
    private const val TAG = "FpsTool"


    private lateinit var mProcessName: String
    private lateinit var mActivity: String

    /**
     * 标准帧数
     */
    private const val FRAME_PER_SECOND = 60

    /**
     * 帧标准间隔
     */
    private const val FPS_PERIOD: Double = 16666.0

    /**
     * 起始时间所在列
     */
    private var mStartPos = -1

    /**
     * 结束时间所在列
     */
    private var mEndPos = -1


    /**
     * 获取top activity
     */
    private fun getTopActivity() {
        try {
            val command = "dumpsys activity top | grep ACTIVITY"
            val process = Runtime.getRuntime().exec(command)
            val reader = BufferedReader(InputStreamReader(process.inputStream))
            var line: String?
            val pattern =
                Pattern.compile("ACTIVITY ([\\w.]+/[\\w.]+)") // 匹配以 “ACTIVITY” 开头，后跟一个包名和一个类名的字符串
            var topActivity = ""
            while (reader.readLine().also { line = it } != null) {
                val matcher = pattern.matcher(line)
                if (matcher.find()) {
                    topActivity = matcher.group(1)
//                    Log.i(TAG, "Top Activity: $topActivity") // 打开检测当前栈顶activity
                }
            }
            val string = topActivity.split("/")
            if (string.size == 2) {
                mProcessName = string[0]
                mActivity = string[1]
            } else {
                mProcessName = "-"
                mActivity = "-"
            }
            Log.i(TAG, "getTopActivity: $mProcessName    $mActivity")
            reader.close()
            process.destroy()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }


    /**
     * 加载  dumpsys gfxinfo
     */
    @JvmStatic
    private fun getFpsTotalData(): String {

        getTopActivity()
        //dumpsys gfxinfo com.cvte.sunkit | grep .MainActivity -A129 | grep Draw -B1 -A128
        //dumpsys gfxinfo com.cvte.sunkit | grep .MainActivity .*visibility=0 -A129 | grep Draw -B1 -A128

        //dumpsys gfxinfo com.cvte.sunkit framestats | grep .MainActivity -A280
        //dumpsys gfxinfo com.android.mxlauncher3 framestats | grep com.android.launcher3.Launcher -A280
        //dumpsys gfxinfo com.dss.teamboard framestats | grep .activity.PlainWhiteboardActivity -A280

        //com.alibaba.dingtalk.focus/.mozi.activity.TeleVideoConfActivity
        //dumpsys gfxinfo com.alibaba.dingtalk.focus framestats | grep .mozi.activity.TeleVideoConfActivity -A280
        val cmd = "dumpsys gfxinfo $mProcessName framestats | grep $mActivity -A280"

        try {
            val process = Runtime.getRuntime().exec(cmd)
            val reader = BufferedReader(InputStreamReader(process.inputStream), 1000)
            var line: String
            val result = StringBuffer("")
            if (reader.readLine() == null) {
                return ""
            }

            while (reader.readLine().also { line = it } != "---PROFILEDATA---") { // 找到数据的起始位置
                continue
            }
            while (reader.readLine().also { line = it } != "---PROFILEDATA---") {
                result.append(line).append("\n")
            }
//            Log.i(TAG, "getFpsTotalData: $result") // 输出采集到的结果
            /**
             *  数据格式如下：
            Flags,IntendedVsync,Vsync,OldestInputEvent,NewestInputEvent,HandleInputStart,AnimationStart,PerformTraversalsStart,DrawStart,SyncQueued,SyncStart,IssueDrawCommandsStart,SwapBuffers,FrameCompleted,DequeueBufferDuration,QueueBufferDuration,
            0,71951690002454,71951740002452,9223372036854775807,0,71951753513667,71951753607000,71951754386042,71951776714294,71951824490758,71951825231008,71951825358466,71952027196486,71952037416779,94704000,526000,
            0,71951756669164,71952090002484,9223372036854775807,0,71952103951369,71952104184994,71952106991703,71952108159244,71952139339998,71952139386373,71952139455206,71952142171206,71952151982457,248000,535000,
            0,71952223318200,71952223318200,9223372036854775807,0,71952224694214,71952224809714,71952224814381,71952250746175,71952255257092,71952255344759,71952255602009,71952270247594,71952280135678,403000,341000,
            0,71953390046789,71953390046789,9223372036854775807,0,71953390669373,71953390751040,71953390755707,71953398167541,71953399902958,71953400016999,71953400105666,71953402672041,71953410408792,176000,332000,
            0,71954590006463,71954590006463,9223372036854775807,0,71954591012202,71954591129744,71954591134702,71954599149995,71954600853037,71954600967078,71954601054287,71954602920370,71954605361620,228000,381000,
             */
            reader.close()
            return result.toString()
        } catch (ex: Exception) {
            ex.printStackTrace()
            Log.e(TAG, "load [dumpsys gfxinfo] exception : $ex")
        }
        return ""

    }

    /**
     *
     * 得到有用的  开始和结束时间
     * 并计算帧率
     */
    fun getStartAndEndTime(): FpsDataWrapper {

        val result = getFpsTotalData() // 裁剪数据  找到起始时间和结束时间的所在列
        if (result == "") {
            return FpsDataWrapper(
                0, 0, 0, 0.0
            )
        }
        val draws: List<String> = result.split("\n".toRegex())
        if (mStartPos == -1 || mEndPos == -1) {
            val titleLine: Array<String> =
                draws[0].split(Regex(",")).toTypedArray()
            for (i in titleLine.indices) {
                if ((titleLine[i] == "IntendedVsync")) {
                    mStartPos = i
                } else if (titleLine[i] == "FrameCompleted") {
                    mEndPos = i
                }
            }
        }
        val startRenderTimes = mutableListOf<Long>()
        val endRenderTimes = mutableListOf<Long>()
        val drawIndex = mutableListOf<Int>() // 记录这一帧的信息在draw数组中的下标

        for (i in 1 until draws.size) {
            val currentLine = draws[i]

            if (!currentLine.startsWith("0")) { // flag如果非零，则该行应该被忽略，因为该帧的预期布局和绘制时间超过16ms，为异常帧
                continue
            }

            val startRenderTime =
                draws[i].split(Regex(",")).toTypedArray()[mStartPos].toLong() / 1000 // 纳秒转换为微秒
            val endRenderTime = draws[i].split(Regex(",")).toTypedArray()[mEndPos].toLong() / 1000
            startRenderTimes.add(startRenderTime)
            endRenderTimes.add(endRenderTime)
            drawIndex.add(i)
        }

        if (startRenderTimes.size < 1 || endRenderTimes.size < 1) {
            return FpsDataWrapper(
                0, 0, 0, 0.0
            )
        }

        val lastPos = startRenderTimes.size - 1 // 原理：找到最后一帧的开始时间，往前寻找1s内屏幕刷新的帧数，先找到最后一帧的下标
//        val filter = endRenderTimes[lastPos] - 1000000L // 它存储了最后一个渲染结束时间减去 1000000 微秒（也就是 1 秒）后的值。这个值用于过滤出满足满帧要求的时间范围。

        var totalCount = 0 // 总帧数计数器，用于统计总共处理的帧数
        var maxJunk: Long = 0 // 最大卡顿时间，用于记录最大的渲染耗时
        var junkCount = 0 // 卡顿帧数计数器，用于统计出现卡顿的帧数。
        var junkVsyncCount = 0 // 卡顿次数计数器，用于统计卡顿次数，每次卡顿可能会持续多个 VSync 周期

        var position: Int = lastPos // 从最后一位向上计数，直到耗时满足满帧  从最后一个位置开始向前迭代，直到遇到满足满帧要求的时间范围为止
        var oneCount = 1000000L //1s
        while (position > -1 && oneCount > 0 && totalCount < 60) {// 一秒钟获得的帧率信息可能多于60行（帧），猜测可能是硬件加速的原因，totalCount保证在行数多余60行时，取这一秒的帧率为标准帧数
//            Log.i(TAG, "起始数据: ${startRenderTimes.joinToString()}")
//            Log.i(TAG, "结束数据: ${endRenderTimes.joinToString()}")
//            Log.i(TAG, "当前行的数据:${draws[drawIndex[position]]} ")
//            Log.i(
//                TAG,
//                "起始时间${startRenderTimes[position]}  结束时间${endRenderTimes[position]}  此帧的执行时间${endRenderTimes[position] - startRenderTimes[position]}: "
//            )
            val junkTime =
                endRenderTimes[position] - startRenderTimes[position] // 计算当前帧的渲染时间，即渲染结束时间减去渲染开始时间。
            if (abs(junkTime) > 1000000) { // 如果某一帧的处理时间超过了1s，此次数据肯定有问题
                position--
                continue
            }
            if (junkTime < 0) { // 某一帧的执行时间是负值，猜测和硬件加速也有原因，导致帧的预期开始时间晚于帧的结束时间（即帧提前完成了）
                totalCount++
                position--
                continue
            }
            oneCount -= junkTime
            if (oneCount < 0) {
                break
            }
            totalCount++ // 增加总帧数计数器。
            val count =
                ceil(junkTime / FPS_PERIOD).toInt() // 根据渲染耗时计算出帧数，使用 Math.ceil 函数将浮点数结果向上取整为整数
            if (junkTime > maxJunk) { // 更新最大卡顿时间，如果当前渲染耗时大于最大卡顿时间则更新为当前渲染耗时。
                maxJunk = junkTime
            }
            if (count > 1) { // 如果帧数超过1，则说明发生了卡顿，增加卡顿帧数计数器。
                junkCount++
            }
            junkVsyncCount += count // 将计算出的帧数累加到卡顿次数计数器，每次卡顿可能持续多个 VSync 周期。
            position--
        }
        val fps =
            if (junkVsyncCount < FRAME_PER_SECOND) FRAME_PER_SECOND - junkVsyncCount + totalCount else totalCount // 标准帧数 - 卡顿次数 + 总帧数：总帧数
        /*Log.e(
            TAG,
            "返回数据: 总帧数${totalCount}   帧率$fps  延迟数$junkCount   最长延迟时间${
                ceil((maxJunk / 1000f).toDouble())
                    .toInt()
            }  延迟占比 ${junkCount / totalCount.toDouble() * 100}"
        )*/
        /**
         * 帧率  fps
         * 延迟数 junkCount
         * 最长延迟数 maxJunk / 1000F
         * 延迟占比 junkCount / (float) totalCount * 100
         */
        val multiplier = 10.0.pow(2)

        return FpsDataWrapper(
            fps, junkCount, ceil((maxJunk / 1000f).toDouble()).toInt(),
            round(junkCount / totalCount.toDouble() * 100 * multiplier) / multiplier
        )
    }

    class FpsDataWrapper(
        var fps: Int,
        var junkCount: Int,
        var maxJunk: Int,
        var junkPercent: Double,
    )
}